﻿using System;
using System.Linq;
using System.Text;
using System.Collections.Generic;
using cludg.Infrastructure.Core.Logging;
using cludg.Infrastructure.Core.Settings;
using nsadv.Statistic.Domain;
using nsadv.Statistic.Repository;
using nsadv.Statistic.Services.PList;

namespace nsadv.Statistic.Services
{
	public class Statistic: Repository.IStatistic
	{
		protected readonly IRepository<StatisticItem> Repository;
		protected readonly ITransactionFactory TransactionFactory;
		protected readonly IStatistic StatisticRepository;
		protected readonly Classifier<Advertiser> Advertisers;
		protected readonly Classifier<Banner> Banners;
		protected readonly Classifier<Place> Places;
		protected readonly Classifier<Platform> Platforms;
		protected readonly Classifier<Campaign> Campaigns;
		protected readonly ILogger Logger;
		protected readonly ISettings Settings;

		public Statistic(
			IRepository<StatisticItem> repository,
			ITransactionFactory transactionFactory,
			IStatistic statisticRepository,
			Classifier<Advertiser> advertisers,
			Classifier<Banner> banners,
			Classifier<Place> places,
			Classifier<Platform> platforms,
			Classifier<Campaign> campaigns,
			ILogger logger,
			ISettings settings)
		{
			Repository = repository;
			TransactionFactory = transactionFactory;
			StatisticRepository = statisticRepository;
			Advertisers = advertisers;
			Banners = banners;
			Places = places;
			Platforms = platforms;
			Campaigns = campaigns;
			Logger = logger;
			Settings = settings;
		}

		protected bool CheckHash(string id, string eventDate, string hash)
		{
			var hashKey = Settings["Statistic_Hash_Key"];

			var s = id + hashKey + eventDate;
			var md5 = System.Security.Cryptography.MD5.Create();

			var data = md5.ComputeHash(Encoding.UTF8.GetBytes(s));
			var builder = new StringBuilder();
			foreach (var b in data)
				builder.Append(b.ToString("x2"));
			var realHash = builder.ToString();

			return hash == realHash;
		}

		public bool TryParseStatistic(string document)
		{
			try
			{
				ParseStatistic(document);
				return true;
			}
			catch (Exception e)
			{
				Logger.Error("Parse statistic error", e);
				return false;
			}
		}

		public void ParseStatistic(string document)
		{
			Logger.Debug(String.Format("try parse statistic/n{0}", document));
			var plist = new ApplePList(document);

			var deviceId = (string)plist.Root["DeviceID"];
			var deviceLanguge = (string)plist.Root["DeviceLanguge"];
			var deviceType = (string)plist.Root["DeviceType"];
			var deviceOS = (string)plist.Root["DeviceOS"];

			var events = (ApplePListArray)plist.Root["EventArray"];
			foreach (var e in events.Cast<ApplePListDict>())
			{
				var eventBeginDate = (DateTime)e["EventBeginDate"];
				var bannerID = (string)e["BannerID"];
				var eventID = (string)e["EventID"];
				var advPlatformID = (string)e["AdvPlatformID"];
				var hash = (string)e["Hash"];
				var count = (int)e["Count"];

				var client = (string)e["Client"];

				var banner = Banners.ById(bannerID);
				var platform = Platforms.ById(deviceType);
				var place = Places.ById(advPlatformID);

				if (banner == null)
					throw new ApplicationException(String.Format("Send statistic. Не найден банер {0}", bannerID));
				if (place == null)
					throw new ApplicationException(String.Format("Send statistic. Не найдена рекламная площадка {0}", advPlatformID));
				if (platform == null)
					throw new ApplicationException(String.Format("Send statistic. Не найдена платформа {0}", platform));

				var item = new StatisticItem
							{
								DeviceId = deviceId,
								DeviceLanguge = deviceLanguge,
								DeviceOS = deviceOS,
								DeviceType = deviceType,
								EventDate = eventBeginDate,
								ClientName = client,
								Banner = banner,
								Count = count,
								Platform = platform,
								Place = place,
								Hash = hash,
								EventId = int.Parse(eventID)
							};
 
				if ((bool)Settings["Statistic_NeedHash"] && !CheckHash(bannerID, e.GetStringValue("EventBeginDate"), hash))
					throw new ApplicationException("Send statistic. Ошибка хэша");

				using (var tran = TransactionFactory.BeginTransaction())
				{
					Repository.Add(item);
					tran.Commit();
				}

				Logger.Info(string.Format("New statistic - {0}", item));			
			}
		}

		public IQueryable<StatisticItem> Query(
			string advertiserId,
			string placeId,
			string platformId, 
			string campaignId, 
			bool? active)
		{
			var result = Repository.All;

			if (!string.IsNullOrEmpty(advertiserId))
				result = result.Where(x => x.Banner.Campaign.Advertiser.Id == advertiserId);

			if (!string.IsNullOrEmpty(placeId))
				result = result.Where(x => x.Place.Id == placeId);

			if (!string.IsNullOrEmpty(platformId))
				result = result.Where(x => x.Platform.Id == platformId);

			if (!string.IsNullOrEmpty(campaignId))
				result = result.Where(x => x.Banner.Campaign.Id == campaignId);

			if (active != null)
			{
				if (active.Value)
					result = result.Where(x => x.Banner.Campaign.IsActive);
				else
					result = result.Where(x => !x.Banner.Campaign.IsActive);
			}

			return result;
		}

		#region IStatistic Members

		public IEnumerable<BannerPeriodicalRow> BannerPeriodical(Banner banner, Platform platform, Advertiser adv,
			Place place, PeriodicalGroupping groupping, DateTime? from = null, DateTime? to = null)
		{
			return StatisticRepository.BannerPeriodical(banner, platform, adv, place, groupping, from, to);
		}

		#endregion
	}
}
